/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.sentry;

import io.sentry.protocol.SentryStackFrame;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;

/**
 * class responsible for converting Java StackTraceElements to SentryStackFrames
 *
 * @since 2021-06-02
 **/
final class SentryStackTraceFactory {
    /**
     * list of inApp excludes
     */
    private final @Nullable List<String> inAppExcludes;

    /**
     * list of inApp includes
     */
    private final @Nullable List<String> inAppIncludes;

    SentryStackTraceFactory(
        @Nullable final List<String> inAppExcludes, @Nullable List<String> inAppIncludes) {
        this.inAppExcludes = inAppExcludes;
        this.inAppIncludes = inAppIncludes;
    }

    /**
     * convert an Array of Java StackTraceElements to a list of SentryStackFrames
     *
     * @param elements Array of Java StackTraceElements
     * @return list of SentryStackFrames or null if none
     */
    @Nullable
    List<SentryStackFrame> getStackFrames(@Nullable final StackTraceElement[] elements) {
        List<SentryStackFrame> sentryStackFrames = null;

        if (elements != null && elements.length > 0) {
            sentryStackFrames = new ArrayList<>();
            for (StackTraceElement item : elements) {
                if (item != null) {
                    // we don't want to add our own frames
                    final String className = item.getClassName();
                    if (className.startsWith("io.sentry.")
                        && !className.startsWith("io.sentry.samples.")
                        && !className.startsWith("io.sentry.mobile.")) {
                        continue;
                    }
                    final SentryStackFrame sentryStackFrame = new SentryStackFrame();
                    sentryStackFrame.setInApp(isInApp(className));
                    sentryStackFrame.setModule(className);
                    sentryStackFrame.setFunction(item.getMethodName());
                    sentryStackFrame.setFilename(item.getFileName());
                    if (item.getLineNumber() >= 0) {
                        sentryStackFrame.setLineno(item.getLineNumber());
                    }
                    sentryStackFrame.setNative(item.isNativeMethod());
                    sentryStackFrames.add(sentryStackFrame);
                }
            }
            Collections.reverse(sentryStackFrames);
        }

        return sentryStackFrames;
    }

    /**
     * Returns if the className is InApp or not.
     *
     * @param className the className
     * @return true if it is or false otherwise
     */
    @TestOnly
    boolean isInApp(final @Nullable String className) {
        if (className == null || className.isEmpty()) {
            return true;
        }

        if (inAppIncludes != null) {
            for (String include : inAppIncludes) {
                if (className.startsWith(include)) {
                    return true;
                }
            }
        }
        if (inAppExcludes != null) {
            for (String exclude : inAppExcludes) {
                if (className.startsWith(exclude)) {
                    return false;
                }
            }
        }
        return false;
    }
}
